// shadersApp.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include "windows.h"
#include <GL/glew.h>
#include <GL/glut.h>
#include <iostream>
#include <vector>
#include "ObjLoader.h"
#include "textfile.h"

GLint v, f, p, tc, te, g;
GLint renderMode = 0;

// Initial size of graphics window.
const int WIDTH = 800;
const int HEIGHT = 600;

const GLuint positionSlot = 0;
const GLuint normalSlot = 0;
GLuint VAO,VBO;
GLuint positionsVbo;
GLuint normalsVbo;

// Current size of window.
int width = WIDTH;
int height = HEIGHT;

static int fovy = 40;

ObjLoader *objects;

const float vertexPosition[] ={
	0.75f, 0.75f, 0.0f, /*1.0f,*/
	0.75f, -0.75f, 0.0f, /*1.0f,*/
	-0.75f, -0.75f, 0.0f, /*1.0f,*/
};

float *objArray;

using namespace std;

void setupMaterial(){
	float diff[4] = { 1.0, 0.3, 0.5, 0 }, spec[4] = { 1, 1, 1, 0 };

	glMaterialfv(GL_FRONT, GL_SPECULAR, spec);
	glMaterialfv(GL_FRONT, GL_DIFFUSE, diff);
	glMaterialf(GL_FRONT, GL_SHININESS, 125);

	float diff2[4] = { 0.3, 0.3, 0.5, 0 };

	glMaterialfv(GL_BACK, GL_SPECULAR, spec);
	glMaterialfv(GL_BACK, GL_DIFFUSE, diff2);
	glMaterialf(GL_BACK, GL_SHININESS, 125);
	// the shininess factor is a number between 0 and 128, where 0 is the shiniest the object can be
}

void setupLights(){
	glMatrixMode(GL_MODELVIEW);
	glPushMatrix();
	glLoadIdentity();

	GLfloat light_position1[] = { 0, 0, 0, 1 };
	GLfloat light1[] = { 1, 1, 1, 1 };
	GLfloat light2[] = { 0.5, 0.5, 0.5, 1.0 };

	//ambient light is reflected environmental light 
	GLfloat ambient[] = { 0.2, 0.2, 0.2, 0 };

	// setup 
	glEnable(GL_LIGHTING);
	glEnable(GL_LIGHT0);
	glShadeModel(GL_SMOOTH);

	glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER, GL_TRUE);
	glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_TRUE);

	glLightf(GL_LIGHT0, GL_SPOT_EXPONENT, 10);
	glLightfv(GL_LIGHT0, GL_SPECULAR, light2);
	glLightfv(GL_LIGHT0, GL_DIFFUSE, light1);
	glLightfv(GL_LIGHT0, GL_AMBIENT, ambient);
	glLightfv(GL_LIGHT0, GL_POSITION, light_position1);

	glPopMatrix();
}

void setShaders()
{
	char *vs = NULL, *fs = NULL, *tcs = NULL, *gs = NULL, *tes = NULL;

	v = glCreateShader(GL_VERTEX_SHADER);
	//tc = glCreateShader(GL_TESS_CONTROL_SHADER);
	//te = glCreateShader(GL_TESS_EVALUATION_SHADER);
	f = glCreateShader(GL_FRAGMENT_SHADER);

	vs = textFileRead("vertShader.vert");
	//tcs = textFileRead("phong.tesscont");
	//tes = textFileRead("phong.tesseval");
	fs = textFileRead("fragShader.frag");

	const char * vv = vs;
	const char * ff = fs;
	//const char * tcc = tcs;
	//const char * tee = tcs;

	glShaderSource(v, 1, &vv, NULL);
	//glShaderSource(tc, 1, &tcc, NULL);
	//glShaderSource(te, 1, &tee, NULL);
	glShaderSource(f, 1, &ff, NULL);

	free(vs); free(fs); free(tcs); free(tes);

	glCompileShader(v);
	//glCompileShader(tc);
	//glCompileShader(te);
	glCompileShader(f);

	p = glCreateProgram();

	glAttachShader(p, v);
	//glAttachShader(p, tc);
	//glAttachShader(p, te);
	glAttachShader(p, f);

	glLinkProgram(p);
	glUseProgram(p);

}
void initializeVertexBuffer()
{
	glGenBuffers(1, &VBO);
	glBindBuffer(GL_ARRAY_BUFFER, VBO);
	glBufferData(GL_ARRAY_BUFFER, sizeof(vertexPosition), vertexPosition, GL_STATIC_DRAW);
	glBindBuffer(GL_ARRAY_BUFFER, 0);
}
void drawObjects()
{
	// Matrix setup
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	glTranslatef(0.0,0.0,-10);
	
	if (renderMode == 0)
	{
		glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
		setShaders();
	}
	else
		glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
	
	/*glGenVertexArrays(1, &VAO);
	glBindVertexArray(VAO);

	glGenBuffers(1, &VBO);
	glBindBuffer(GL_ARRAY_BUFFER, VBO);

	glBufferData(GL_ARRAY_BUFFER, sizeof(objects->vertices), &objects->vertices[0], GL_STATIC_DRAW);
	glVertexAttribPointer(positionSlot, 2, GL_FLOAT, GL_FALSE, 0, 0);
	glPatchParameteri(GL_PATCH_VERTICES, 3);
	glDrawArrays(GL_PATCHES, 0, 8);
	*/
	/*glGenBuffers(1, &VBO);
	glBindBuffer(GL_ARRAY_BUFFER, VBO);
	glBufferData(GL_ARRAY_BUFFER, sizeof(vertexPosition), vertexPosition, GL_STATIC_DRAW);
	glBindBuffer(GL_ARRAY_BUFFER, 0);

	glBindBuffer(GL_ARRAY_BUFFER, VBO);
	glEnableVertexAttribArray(0);
	glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
	//glPatchParameteri(GL_PATCH_VERTICES, 3);
	//glDrawArrays(GL_PATCHES, 0, 9);
	glDrawArrays(GL_TRIANGLES, 0, 3);
	*/
	
	glPushMatrix();

	for (int i = 0; i < objects->faces.size(); i++)
	{
		glBegin(GL_TRIANGLES);
		for (int j = 0; j < 3; j++){

			glNormal3f(objects->v_normals[objects->faces[i][j][2]][0],
				objects->v_normals[objects->faces[i][j][2]][1],
				objects->v_normals[objects->faces[i][j][2]][2]);

			glVertex3f(objects->vertices[objects->faces[i][j][0]][0],
				objects->vertices[objects->faces[i][j][0]][1],
				objects->vertices[objects->faces[i][j][0]][2]);
		}
		glEnd();
	}
	glPopMatrix();

	glUseProgram(0);

}

void display()
{
	//Background color
	glClearColor(1, 1, 1, 0);
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	glEnable(GL_DEPTH_TEST);

	// Matrix setup
	glMatrixMode(GL_PROJECTION);
	glViewport(0, 0, width, height);
	glLoadIdentity();
	gluPerspective(fovy, (float)width / (float)height, 0.1, 1000);

	drawObjects();

	glutSwapBuffers();
}

void reshapeMainWindow(int newWidth, int newHeight)
{
	width = newWidth;
	height = newHeight;
	glutPostRedisplay();
}

void keyboard(unsigned char key, int x, int y) {

	switch (key)
	{
	case '1':
		renderMode = 0;
		cout << "renderMode: " << renderMode+1 <<  " shading mode" << endl;
		glutPostRedisplay();
		break;
	case '2':
		renderMode = 1;
		cout << "renderMode: " << renderMode+2 << " wireframe mode" << endl;
		glutPostRedisplay();
		break;
	/*case('1') :
		shadingMode = 0;
		setShaders();
		cout << "shader1" << endl;
		glutPostRedisplay();
		break;
	case('2') :
		shadingMode = 1;
		setShaders();
		cout << "shader2" << endl;
		glutPostRedisplay();
		break;
	case(27) :*/
	case 'q':
		exit(0);
		break;
	}
	//AssistModifiers();
}

void initialize(){
	//loads object
	objects = new ObjLoader("obj/monkeyFace.obj");
	
	//initializeVertexBuffer();
	setupMaterial();
	setupLights();
}

void main(int argc, char **argv)
{
	// GLUT initialization.
	glutInit(&argc, argv);
	glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB);
	glutInitWindowSize(width, height);
	glutCreateWindow("Shaders App");

	initialize();
	glutKeyboardFunc(keyboard);
	glutDisplayFunc(display);
	glutReshapeFunc(reshapeMainWindow);
	
	glewExperimental = GL_TRUE;
	glewInit();

	if (glewIsSupported("GL_VERSION_4_0"))
		printf("Ready for OpenGL 4.0\n");
	//setShaders();
	// Enter GLUT loop.
	glutMainLoop();

	delete objects;

}

